﻿//-----------------------------------------------------------------------
// <copyright file="TupleMonadLinkedList.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the TupleMonadLinkedList class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.Tuples
{
    using System;
    using System.Diagnostics;

    /// <summary>
    /// Represents a strongly typed list of objects that can be accessed by index.
    /// Provides methods to search, sort, and manipulate lists.
    /// </summary>
    [Serializable]
    [DebuggerDisplay("Count = {Count}")]
    public class TupleMonadLinkedList
    {
        /// <summary>
        /// Count of elements contained in the list.
        /// </summary>
        public int Count;

        /// <summary>
        /// The head tuple in the list.
        /// </summary>
        public Tuple First;

        /// <summary>
        /// The tail tuple in the list.
        /// </summary>
        public Tuple Last;

        /// <summary>
        /// The index of the linked list.
        /// </summary>
        private int index;

        /// <summary>
        /// The current tuple in the list.
        /// </summary>
        private Tuple current;

        /// <summary>
        /// Adds an object to the head of the Tulip.Engine.Tuples.TupleLinkedList.
        /// </summary>
        /// <param name="tuple">
        /// The tuple to be added to the head of the Tulip.Engine.Tuples.TupleLinkedList.
        /// </param>
        public void Add(Tuple tuple)
        {
            if (tuple == null)
            {
                return;
            }

            if (this.First == null)
            {
                this.First = this.Last = tuple;
            }
            else
            {
                var currentTail = this.Last;
                tuple.MonadTuplePrevious = currentTail;
                currentTail.MonadTupleNext = tuple;
                this.Last = tuple;
            }

            this.Count++;
        }

        /// <summary>
        /// Returns the next tuple in the linked list.
        /// </summary>
        /// <returns>The next tuple in the linked list.</returns>
        public Tuple GetNext()
        {
            if (this.First == default(Tuple))
            {
                // reset
                this.current = default(Tuple);
                this.index = 0;
                return default(Tuple);
            }

            if (this.current == default(Tuple))
            {
                this.current = this.First;
                this.index++;
                return this.current;
            }

            if (this.index < this.Count)
            {
                this.current = this.current.MonadTupleNext;
                this.index++;
                return this.current;
            }

            // reset
            this.current = default(Tuple);
            this.index = 0;
            return default(Tuple);
        }

        /// <summary>
        /// Returns the next tuple of a given type from the linked list.
        /// </summary>
        /// <param name="type">The type of tupel to return.</param>
        /// <returns>A tuple of the given type.</returns>
        public Tuple GetNext(Type type)
        {
            Tuple outTuple;

            do
            {
                outTuple = this.GetNext();
            }
            while (!(outTuple == null || outTuple.GetType() == type));

            return outTuple;
        }

        /// <summary>
        /// Removes all elements from the Tulip.Engine.Tuples.TupleLinkedList.
        /// </summary>
        public void Clear()
        {
            if (this.First == null || this.Count <= 0)
            {
                return;
            }

            var currentTuple = this.First;

            for (var idx = 0; idx < this.Count; idx++)
            {
                currentTuple = this.RemoveTuple(currentTuple);
            }

            this.First = null;
            this.Last = null;
        }

        /// <summary>
        /// Determines whether an element is in the Tulip.Engine.Tuples.TupleLinkedList.
        /// </summary>
        /// <param name="tuple">
        /// The tuple to locate in the Tulip.Engine.Tuples.TupleLinkedList. The value
        /// can be null for reference types.
        /// </param>
        /// <returns>
        /// true if item is found in the Tulip.Engine.Tuples.TupleLinkedList; otherwise,
        /// false.
        /// </returns>
        public bool Contains(Tuple tuple)
        {
            var existingTuple = this.First;

            for (var count = 0; count < this.Count; count++)
            {
                if (tuple == existingTuple)
                {
                    return true;
                }

                existingTuple = existingTuple.MonadTupleNext;
            }

            return false;
        }

        /// <summary>
        /// Removes and returns a tuple from the list.
        /// </summary>
        /// <param name="tuple">The tuple to be removed.</param>
        /// <returns>The tuple removed from the list.</returns>
        private Tuple RemoveTuple(Tuple tuple)
        {
            var nextTuple = tuple.MonadTupleNext;
            var prevTuple = tuple.MonadTuplePrevious;

            if (nextTuple == null)
            {
                if (prevTuple == null)
                {
                    this.Last = null;
                    this.First = null;
                }
                else
                {
                    prevTuple.MonadTupleNext = null;
                    this.Last = prevTuple;
                }
            }
            else
            {
                if (prevTuple == null)
                {
                    nextTuple.MonadTuplePrevious = null;
                    this.First = nextTuple;
                }
                else
                {
                    nextTuple.MonadTuplePrevious = prevTuple;
                    prevTuple.MonadTupleNext = nextTuple;
                }
            }

            this.Count--;

            return nextTuple;
        }
    }
}
